package edu.scsu.csci.controller;

import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import edu.scsu.csci.IController;
import edu.scsu.csci.context.ActionContext;
import edu.scsu.csci.context.ViewContext;
import edu.scsu.csci.core.ActionMethod;
import edu.scsu.csci.core.ApplicationConfiguration;
import edu.scsu.csci.core.ActionURL;
import edu.scsu.csci.interceptor.EInterceptorFlow;

/**
 *
 * @author Maqbool Khan
 * @since jdk 1.5
 * @see
 * @viz.diagram AbstractController.tpx
 */
public abstract class AbstractController implements IController{
	
	/**
	 * servlet context
	 */
	protected ServletContext servletContext;
	

	
	/* (non-Javadoc)
	 * @see edu.scsu.csci.IController#intialize(javax.servlet.ServletContext)
	 */
	public void intialize(ServletContext servletContext) {
		this.servletContext = servletContext;
	}
	
	/**
	 * <p> Acts as a template method for the concrete controllers.
	 * @see edu.scsu.csci.IController#execute(java.lang.String, javax.servlet.ServletRequest, javax.servlet.ServletResponse)
	 */
	public boolean execute(ActionURL commandURL, ServletRequest request, ServletResponse response) {
		
		//clear the errors from the last request
		if( WebUtils.getExecutionErrors() != null) {
			WebUtils.getExecutionErrors().clear();
		}
		
		//get method annotations by creating the operation context
		ActionContext operationContext = ActionContext.getInstance(commandURL, request, response);
		
		boolean isActionInvocationAllowed = isActionInvocationAllowed(operationContext.getServiceObject(), 
												operationContext.getActionMethod());
		
		if(!isActionInvocationAllowed){
			WebUtils.setExecutionError("SYSTEM", "The Action cannot be invoked because of security policy.");
			return returnWithErrors(operationContext);
		}
		
		Object domainObject = null;
		
		if( operationContext.getDomainObjectClazz() != null){
			//populate the domain object
			domainObject = populateAndValidateDomainObject(operationContext.getDomainObjectClazz(), request);
			
			if(domainObject == null){
				return returnWithErrors(operationContext);
			}
		}
		
		if(!performBuisnessValidation(domainObject, operationContext)){
			return returnWithErrors(operationContext);
		}
		
		//invoke the interceptors for pre processing
		preProcessInterceptors(domainObject, request, response,operationContext);
		
		//create the buisness logic class and invoke the action
		Object outputBean = invokeAction(operationContext.getServiceObject(), domainObject, operationContext.getActionMethod());
		
		//invoke the interceptors for post processing
		postProcessInterceptors(domainObject, request, response, operationContext);
		
		//set domain object to proper scope
		setObjects(operationContext, domainObject, outputBean);
		
		//invoke the view with the view context and domain object map
		delegateToView(operationContext.getServletRequest(), 
					operationContext.getServletResponse(), operationContext.getViewContext());
		
		return true;
	}
	
	private boolean returnWithErrors(ActionContext operationContext){
		setErrors(operationContext);
		delegateToView(operationContext.getServletRequest(), 
					operationContext.getServletResponse(), operationContext.getViewContext());
		return false;
	}
	
	
	/**
	 * @param object
	 * @return
	 */
	abstract protected boolean isActionInvocationAllowed(Object businessObject, ActionMethod actionMethod);
	
	/**
	 * @param operationContext
	 */
	abstract protected void setErrors(ActionContext operationContext);
	
	
	/**
	 * <p>	Sets the domain object and the returned object from action invocation into one of the following
	 *  scopes.
	 *  <ul>
	 *  	<li>Request</li>
	 *  	<li>Session</li>
	 *  	<li>Application</li>
	 *  </ul>
	 * 
	 * @param operationContext
	 * @param domainObject
	 * @param returnedObject
	 */
	abstract protected void setObjects(ActionContext operationContext, Object domainObject, Object returnedObject);
	
	/**
	 * Performs buisness validation on the domain object. 
	 * 
	 * @param domainObject
	 * @return
	 */
	abstract protected boolean performBuisnessValidation(Object domainObject, ActionContext operationContext);
	
	/**
	 * Populates the domain object with the request parameters.
	 * 
	 * @param domainObjectClazz		domain object class whose properties would be set to the parameters 
	 * 								contained in the request.
	 * @param request				
	 * @return
	 */
	abstract protected Object populateAndValidateDomainObject(Class<?> domainObjectClazz, ServletRequest request);
	
	
	/**
	 * @param context
	 * @return
	 */
	abstract protected EInterceptorFlow preProcessInterceptors(Object domainObject, ServletRequest request, 
				ServletResponse response, ActionContext operationContext);
	
	/**
	 * @param context
	 * @return
	 */
	abstract protected EInterceptorFlow postProcessInterceptors(Object domainObject, ServletRequest request, 
				ServletResponse response, ActionContext operationContext);
	
	/**
	 * @return
	 */
	abstract protected Object invokeAction(Object businessObject, Object domainObject, ActionMethod commanMethod);
	
	/**
	 * Delegates the request processing to appropriate view implementation defined by the method annotation. 
	 */
	abstract protected void delegateToView(ServletRequest request, ServletResponse response, ViewContext viewContext);
	
}